# !/usr/bin/env python3
# coding=utf8
"""
TradeData 生成器,
"""
import datetime
import pathlib
from typing import Dict, List, Set, Tuple, Optional, Union, Callable
from vnpy.tools.bar_generator import BarGeneratorVnpy
from vnpy.trader.constant import Direction, Offset, Exchange, Interval
from vnpy.trader.object import TradeData
from vnpy.trader.utility import get_file_path, ZoneInfo


class BDObj(object):
    """ business_day_object => BDObj """

    def __init__(self, business_day) -> None:
        """"""
        self.business_day = business_day


class TradeDataGenerator(object):
    """"""

    def __init__(self, exchange: Union[Exchange, str], symbol: str) -> None:
        """"""
        self.exchange: Exchange = Exchange(exchange) if isinstance(exchange, str) else exchange
        self.symbol: str = symbol

        self.trades: List[TradeData] = []

    def buy(self, trade_price: float, trade_volume: float, dttm: datetime.datetime, business_day: datetime.date) -> None:
        """"""
        order_direction, order_offset = Direction.LONG, Offset.OPEN  # buy
        trade: TradeData = TradeData(
            symbol=self.symbol,
            exchange=self.exchange,
            orderid=str(len(self.trades) + 1),
            tradeid=str(len(self.trades) + 1),
            direction=order_direction,
            offset=order_offset,
            price=trade_price,
            volume=trade_volume,
            datetime=dttm,
            gateway_name="pandas",
        )
        BarGeneratorVnpy.deliver_business_day(dst=trade, src=BDObj(business_day=business_day))
        self.trades.append(trade)

    def sell(self, trade_price: float, trade_volume: float, dttm: datetime.datetime, business_day: datetime.date) -> None:
        """"""
        order_direction, order_offset = Direction.SHORT, Offset.CLOSE  # sell
        trade: TradeData = TradeData(
            symbol=self.symbol,
            exchange=self.exchange,
            orderid=str(len(self.trades) + 1),
            tradeid=str(len(self.trades) + 1),
            direction=order_direction,
            offset=order_offset,
            price=trade_price,
            volume=trade_volume,
            datetime=dttm,
            gateway_name="pandas",
        )
        BarGeneratorVnpy.deliver_business_day(dst=trade, src=BDObj(business_day=business_day))
        self.trades.append(trade)

    def short(self, trade_price: float, trade_volume: float, dttm: datetime.datetime, business_day: datetime.date) -> None:
        """"""
        order_direction, order_offset = Direction.SHORT, Offset.OPEN  # short
        trade: TradeData = TradeData(
            symbol=self.symbol,
            exchange=self.exchange,
            orderid=str(len(self.trades) + 1),
            tradeid=str(len(self.trades) + 1),
            direction=order_direction,
            offset=order_offset,
            price=trade_price,
            volume=trade_volume,
            datetime=dttm,
            gateway_name="pandas",
        )
        BarGeneratorVnpy.deliver_business_day(dst=trade, src=BDObj(business_day=business_day))
        self.trades.append(trade)

    def cover(self, trade_price: float, trade_volume: float, dttm: datetime.datetime, business_day: datetime.date) -> None:
        """"""
        order_direction, order_offset = Direction.LONG, Offset.CLOSE  # cover
        trade: TradeData = TradeData(
            symbol=self.symbol,
            exchange=self.exchange,
            orderid=str(len(self.trades) + 1),
            tradeid=str(len(self.trades) + 1),
            direction=order_direction,
            offset=order_offset,
            price=trade_price,
            volume=trade_volume,
            datetime=dttm,
            gateway_name="pandas",
        )
        BarGeneratorVnpy.deliver_business_day(dst=trade, src=BDObj(business_day=business_day))
        self.trades.append(trade)

    @classmethod
    def sign(cls, x) -> int:
        """计算一个数字的符号(负数:-1, 零:0, 正数:+1)"""
        return -1 if x < 0 else +1 if 0 < x else 0

    def similar_sign_abs_up(self, pos_old: float, pos_new: float, trade_price: float, dttm: datetime.datetime, business_day: datetime.date) -> None:
        """
        同向变大(符号相同/相似,绝对值变大)
        +0=>+1 (+0=>+3)
        +1=>+1 (+3=>+5)
        -0=>-1 (-0=>-3)
        -1=>-1 (-3=>-5)
        """
        pos_delta: float = abs(pos_new) - abs(pos_old)
        assert 0 < pos_delta

        signs: Set[int] = set([self.sign(pos_new), self.sign(pos_old)])
        signs.discard(0)
        assert len(signs) == 1

        sign: int = signs.pop()

        if False:
            pass
        elif sign > 0:
            self.buy(trade_price=trade_price, trade_volume=pos_delta, dttm=dttm, business_day=business_day)
        elif sign < 0:
            self.short(trade_price=trade_price, trade_volume=pos_delta, dttm=dttm, business_day=business_day)
        else:
            assert False

    def similar_sign_abs_dn(self, pos_old: float, pos_new: float, trade_price: float, dttm: datetime.datetime, business_day: datetime.date) -> None:
        """
        同向变小(符号相同/相似,绝对值变小)
        -1=>-1 (-5=>-3)
        -1=>-0 (-3=>-0)
        +1=>+1 (+5=>+3)
        +1=>+0 (+3=>+0)
        """
        pos_delta: float = abs(pos_old) - abs(pos_new)
        assert 0 < pos_delta

        signs: Set[int] = set([self.sign(pos_old), self.sign(pos_new)])
        signs.discard(0)
        assert len(signs) == 1

        sign: int = signs.pop()

        if False:
            pass
        elif sign > 0:
            self.sell(trade_price=trade_price, trade_volume=pos_delta, dttm=dttm, business_day=business_day)
        elif sign < 0:
            self.cover(trade_price=trade_price, trade_volume=pos_delta, dttm=dttm, business_day=business_day)
        else:
            assert False

    def change_position(self, pos_old: float, pos_new: float, trade_price: float, dttm: datetime, business_day: datetime.date) -> None:
        """"""
        assert pos_old != pos_new  # 如果都是0, 会在这里检查出来

        sign_old: int = self.sign(pos_old)
        sign_new: int = self.sign(pos_new)

        if sign_old == sign_new:  # sign 相同 ( +1=>+1 / -1=>-1 )
            if abs(pos_old) < abs(pos_new):
                self.similar_sign_abs_up(pos_old, pos_new, trade_price, dttm, business_day=business_day)
            else:
                self.similar_sign_abs_dn(pos_old, pos_new, trade_price, dttm, business_day=business_day)
        else:  # sign 相反, 或者, sign 含 0
            if False:
                pass
            elif sign_old == 0:  # -0=>-1 / +0=>+1
                self.similar_sign_abs_up(pos_old, pos_new, trade_price, dttm, business_day=business_day)
            elif sign_new == 0:  # -1=>-0 / +1=>+0
                self.similar_sign_abs_dn(pos_old, pos_new, trade_price, dttm, business_day=business_day)
            else:  # -1=>+1 / +1=>-1
                self.similar_sign_abs_dn(pos_old, (0 + 0), trade_price, dttm, business_day=business_day)
                self.similar_sign_abs_up((0 + 0), pos_new, trade_price, dttm, business_day=business_day)

    def save_to_file(self) -> None:
        """"""
        CHINA_TZ = ZoneInfo("Asia/Shanghai")

        lines: List[str] = [
            "成交号,委托号,代码,交易所,方向,开平,价格,数量,时间\n"
        ]

        for trade in self.trades:
            trade: TradeData = trade
            line: str = f"{trade.tradeid},{trade.orderid},{trade.symbol},{trade.exchange.value},{trade.direction.value},{trade.offset.value},{trade.price},{trade.volume},{trade.datetime.replace(tzinfo=CHINA_TZ)}\n"
            lines.append(line)

        filename: str = f"trade_{self.exchange.value}_{self.symbol}_v1.csv"
        filepath: pathlib.Path = pathlib.Path(__file__).parent.joinpath(filename)
        with open(file=filepath, mode="w", encoding="utf8") as f:
            f.writelines(lines)

        lines: List[str] = [
            "代码,交易所,方向,开平,价格,数量,时间\n"
        ]

        for trade in self.trades:
            trade: TradeData = trade
            line: str = f"{trade.symbol},{trade.exchange.value},{trade.direction.value},{trade.offset.value},{trade.price},{trade.volume},{trade.datetime.replace(tzinfo=CHINA_TZ)}\n"
            lines.append(line)

        filename: str = f"trade_{self.exchange.value}_{self.symbol}_v2.csv"
        filepath: pathlib.Path = pathlib.Path(__file__).parent.joinpath(filename)
        with open(file=filepath, mode="w", encoding="utf8") as f:
            f.writelines(lines)


if __name__ == "__main__":
    pass
